<?php

namespace app\common;

use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use think\File;

class ExcelService
{
    /**
     * 导出 Excel 文件
     *
     * @param array  $data       数据内容，二维数组（行、列对应）
     * @param array  $headers    表头（索引数组）
     * @param string $fileName   文件名
     * @param bool   $saveToFile 是否保存到文件（true：保存文件，false：输出到浏览器）
     * @param string $filePath   文件保存路径（可选，默认为当前目录）
     * @return string|void       如果保存到文件，返回文件路径；否则直接输出到浏览器
     */
    public static function export(array $data, array $headers, string $fileName = "export.xlsx", bool $saveToFile = false, string $filePath = '')
    {
        try {
            // 创建 Excel 文件对象
            $spreadsheet = new Spreadsheet();
            $sheet = $spreadsheet->getActiveSheet();

            // 设置表头
            foreach ($headers as $key => $value) {
                $col = self::getColumnLabelFromNumber($key + 1);
                $sheet->setCellValue($col . '1', $value); // 第一行表头
            }

            // 插入数据
            foreach ($data as $rowIndex => $row) {
                foreach ($row as $colIndex => $value) {
                    $col = self::getColumnLabelFromNumber($colIndex + 1); // 列
                    $sheet->setCellValue($col . ($rowIndex + 2), $value);
                }
            }

            $writer = IOFactory::createWriter($spreadsheet, "Xlsx");

            if ($saveToFile) {
                // 保存到文件
                $fullPath = $filePath ?: (getcwd() . DIRECTORY_SEPARATOR . $fileName);
                $writer->save($fullPath);
                return $fullPath; // 返回文件路径
            } else {
                // 输出到浏览器
                header('Content-Type: application/octet-stream');
                header("Content-Disposition: attachment;filename=\"$fileName\"");
                header("Cache-Control: max-age=0");
                $writer->save("php://output");
                exit();
            }
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

    /**
     * 导入 Excel 文件
     *
     * @param string $filePath Excel 文件路径
     * @return array           返回 Excel 数据
     */
    public static function import(string $filePath): array
    {
        try {
            if (!file_exists($filePath)) {
                throw new \Exception("文件不存在：$filePath");
            }

            // 加载 Excel 文件
            $spreadsheet = IOFactory::load($filePath);
            $sheet = $spreadsheet->getActiveSheet();

            // 获取内容
            $data = [];
            foreach ($sheet->getRowIterator() as $rowIndex => $row) {
                $rowData = [];
                foreach ($row->getCellIterator() as $cell) {
                    $rowData[] = $cell->getValue();
                }
                $data[] = $rowData;
            }
            return $data;
        } catch (\Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }

    /**
     * 导入 Excel 文件数据，直接接收上传的文件对象
     *
     * @param File $file 上传的文件对象
     * @return array 返回 Excel 数据或错误信息
     */
    public static function importFromUpload(File $file): array
    {
        try {
            // 检查文件格式
            $originalName = $file->getOriginalName();
            $ext = pathinfo($originalName, PATHINFO_EXTENSION);
            if ($ext !== 'xlsx') {
                throw new \Exception('文件格式不正确，请上传 xlsx 格式的文件');
            }

            // 定义保存路径
            $savePath = runtime_path() . 'uploads/excel/' . date('Ymd');
            if (!is_dir($savePath)) {
                mkdir($savePath, 0777, true); // 确保目录存在
            }

            // 生成唯一文件名
            $uniqueName = uniqid() . '_' . time() . '.' . $ext;
            $fullPath = $savePath . '/' . $uniqueName;

            // 移动文件到指定位置
            if (!$file->move($savePath, $uniqueName)) {
                throw new \Exception('文件移动失败');
            }

            // 加载并返回 Excel 数据
            return self::import($fullPath);
        } catch (\Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }

    private static function getColumnLabelFromNumber(int $number): string
    {
        $prefix = '';
        while ($number > 0) {
            $number--;
            $remainder = $number % 26;
            $prefix = chr(65 + $remainder) . $prefix;
            $number = (int)($number / 26);
        }
        return $prefix;
    }
}
